package cc.rengu.igas.channel.wechat.core.packswitch;

import cc.rengu.igas.channel.wechat.common.constant.WechatConfigConstant;
import cc.rengu.igas.channel.wechat.common.constant.WechatParamConstant;
import cc.rengu.igas.channel.wechat.common.constant.WechatTreeNodeConstant;
import cc.rengu.igas.channel.wechat.common.enums.RespCodeEnum;
import cc.rengu.igas.channel.wechat.core.realize.WechatSignService;
import cc.rengu.igas.channel.wechat.core.realize.impl.WechatSignServiceImpl;
import cc.rengu.igas.channel.wechat.facade.base.BaseRequest;
import cc.rengu.jradp.packetswitch.IncomingPackSwitchImpl;
import cc.rengu.jradp.packetswitch.OutgoingPackSwitchImpl;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.BizMsgInfoMapper;
import cc.rengu.oltp.service.common.dao.CertInfoMapper;
import cc.rengu.oltp.service.common.dao.SecPlanInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.BizMsgInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.CertInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SecPlanInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.*;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.*;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import cc.rengu.utility.xml.XmlDocument;
import cc.rengu.utility.xml.XmlError;
import cc.rengu.utility.xml.XmlValue;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * 与银联第三方通讯解包前、解包后、组包前及组后的处理类
 * Created by
 */
public class CupsWechatClientPackSwitch implements IncomingPackSwitchImpl, OutgoingPackSwitchImpl {

    @Override
    public byte[] beforeInComPackSwitch(byte[] bytes) throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /* 获取数字签名字符串 */
        String xmlBefore = new String(bytes);
        /*更新业务报文信息*/
        updateBizMsgInfo(rglog, xmlBefore);
        XmlDocument xmlDocument = new XmlDocument();
        rglog.debug("接收原始包xmlBefore=[{}]", xmlBefore);
        XmlError xmlError = xmlDocument.loadString(xmlBefore);
        if (!xmlError.isSuccess()) {
            rglog.error("接收报文异常!");
            throw new BizException(RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode(), RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespDesc());
        } else {
            XmlValue xmlvalue = xmlDocument.getValue("sign", xmlError);
            if (null == xmlvalue) {
                rglog.debug("无签域，不进行签名校验!");
                return bytes;
            }
            String signData = xmlvalue.getText();
            if (null == signData) {
                rglog.debug("无签域，不进行签名校验!");
                return bytes;
            }
            /* 生成生成数字签名的字符串 */
            String signBlockString = generateSignBlockString(xmlBefore);
            if (null == signBlockString) {
                rglog.error("生成加签Block失败!");
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_CODE, RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_MSG, RespCodeEnum.VERIFY_SIGN_ERROR.getRespDesc());
                return new byte[0];
                //throw new BizException(RespCodeEnum.GENERATE_SIGN_BLOCK_ERROR.getRespCode(),RespCodeEnum.GENERATE_SIGN_BLOCK_ERROR.getRespDesc());
            }
            rglog.debug("加签Block:<{}>", signBlockString);

            String secPlanInfoListString = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.SEC_PLAN_INFO_LIST);
            List<SecPlanInfo> secPlanInfoList = JSONArray.parseArray(secPlanInfoListString, SecPlanInfo.class);
            Optional<SecPlanInfo> checkSignSecPlanOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(WechatParamConstant.VERIFY_CERT_TYPE)).findFirst();
            if (checkSignSecPlanOpt.isPresent()) {
                CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
                CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(checkSignSecPlanOpt.get().getInstId() + checkSignSecPlanOpt.get().getSecPlanId() + checkSignSecPlanOpt.get().getSecPlanType());
                if (certInfo == null) {
                    rglog.error("获取验签密钥失败!");
                    xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_CODE, RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
                    xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_MSG, RespCodeEnum.VERIFY_SIGN_ERROR.getRespDesc());
                    return new byte[0];
                }
                /* 验证签名 */
                WechatSignService wechatSignService = new WechatSignServiceImpl();
                try {
                    if (wechatSignService.verifySignString(signData, signBlockString, certInfo)) {
                        rglog.info("签名验证成功!");
                        return bytes;
                    } else {
                        rglog.error("签名验证失败!");
                        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_CODE, RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
                        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_MSG, RespCodeEnum.VERIFY_SIGN_ERROR.getRespDesc());
                        return new byte[0];
                    }
                } catch (Exception e) {
                    rglog.error(e.getMessage());
                    xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_CODE, RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
                    xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_MSG, RespCodeEnum.VERIFY_SIGN_ERROR.getRespDesc());
                    return new byte[0];
                }
            } else {
                rglog.error("获取验签证书密钥失败!验签失败");
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_CODE, RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_MSG, RespCodeEnum.VERIFY_SIGN_ERROR.getRespDesc());
                return new byte[0];
            }
        }
    }

    @Override
    public int afterInComPackSwitch() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        if (xmlTreeUtil.isExist(TreeNodeConstant.ORDER_AMT)) {
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.ORDER_AMT, AmountUtil.changeF2Y(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.ORDER_AMT)));
        }
        if (xmlTreeUtil.isExist(TreeNodeConstant.REFUND_AMT)) {
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.REFUND_AMT, AmountUtil.changeF2Y(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.REFUND_AMT)));
        }
        if (xmlTreeUtil.isExist("refundOrderAmt")) {
            xmlTreeUtil.setXmlTreeStringValue("refundOrderAmt", AmountUtil.changeF2Y(xmlTreeUtil.getXmlTreeStringValue("refundOrderAmt")));
        }
        if (xmlTreeUtil.isExist("cashAmt")) {
            xmlTreeUtil.setXmlTreeStringValue("cashAmt", AmountUtil.changeF2Y(xmlTreeUtil.getXmlTreeStringValue("cashAmt")));
        }
        if (xmlTreeUtil.isExist("settleAmt")) {
            xmlTreeUtil.setXmlTreeStringValue("settleAmt", AmountUtil.changeF2Y(xmlTreeUtil.getXmlTreeStringValue("settleAmt")));
        }
        if (xmlTreeUtil.isExist(TreeNodeConstant.REFUND_FEE)) {
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.REFUND_FEE, AmountUtil.changeF2Y(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.REFUND_FEE)));
        }

        return 0;
    }

    @Override
    public int beforeOutgoPackSwitch() throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        if (xmlTreeUtil.isExist(TreeNodeConstant.ORDER_AMT)) {
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.ORDER_AMT, AmountUtil.changeY2F(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.ORDER_AMT)));
        }
        if (xmlTreeUtil.isExist(TreeNodeConstant.REFUND_AMT)) {
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.REFUND_AMT, AmountUtil.changeY2F(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.REFUND_AMT)));
        }
        if (xmlTreeUtil.isExist("refundOrderAmt")) {
            xmlTreeUtil.setXmlTreeStringValue("refundOrderAmt", AmountUtil.changeY2F(xmlTreeUtil.getXmlTreeStringValue("refundOrderAmt")));
        }
        if (xmlTreeUtil.isExist(TreeNodeConstant.REFUND_FEE)) {
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.REFUND_FEE, AmountUtil.changeY2F(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.REFUND_FEE)));
        }
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        if (instId == null) {
            instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID);
        }
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParam = null;
        /* 加载公共信息 */
        sysParam = sysParamService.getSysParamInfo(instId, WechatConfigConstant.CUPS_WECHAT_CONF_NODE, WechatConfigConstant.WECHAT_SIGN_TYPE);
        if (sysParam != null) {
            if ("Y".equals(sysParam.getParamStatus())) {
                rglog.debug("微信对接接口采用最新版本接口，需要配置验签方式sign_type");
                xmlTreeUtil.setXmlTreeStringValue(WechatConfigConstant.WECHAT_SIGN_TYPE, sysParam.getParamValue());
            }
        } else {
            rglog.debug("微信签名方式未配置验签方式，报文不配置sign_type，报文采用老版本对接");
        }
        DstChannelCfg dstChannelCfg = (DstChannelCfg) xmlTreeUtil.getXmlTreeObjectValue(WechatTreeNodeConstant.DST_CHANNEL_CFG, DstChannelCfg.class);
        /* 根据通道安全计划获取安全计划 */
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        List<SecPlanInfo> secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(instId, dstChannelCfg.getSecPlanId());
        if (null != secPlanInfoList && !secPlanInfoList.isEmpty()) {
            xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.SEC_PLAN_INFO_LIST, JSON.toJSONString(secPlanInfoList));
            /*如果配置1.9版本报文接口，需要获取证书序列号上传*/
            if (null != sysParam && "Y".equals(sysParam.getParamStatus())) {
                Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(WechatParamConstant.SIGN_CERT_TYPE)).findFirst();
                if (secPlanInfoOpt.isPresent()) {
                    CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
                    CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(instId + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
                    if (certInfo == null) {
                        rglog.error("证书查询失败");
                        throw new BizException(RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespCode(), RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespDesc());
                    }
                    xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.CERT_ID, certInfo.getCertId());
                }
            }
        } else {
            rglog.error("获取服务商签名密钥失败");
            throw new BizException(RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespCode(), RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespDesc());
        }
        String nonceStr;
        nonceStr = RandomUtil.randomString(32);
        xmlTreeUtil.setXmlTreeStringValue("nonceStr", nonceStr);
        return 0;
    }

    @Override
    public byte[] afterOutgoPackSwitch(byte[] bytes) throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String msgRegisterFlag = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WECHAT_MSG_REG_FLAG);
        String xmlBefore = new String(bytes);
        rglog.info("outBuf = [{}]", xmlBefore);
        String signData = null;
        /* 生成生成数字签名的字符串 */
        String signBlockString = generateSignBlockString(xmlBefore);
        if (null == signBlockString) {
            rglog.error("生成加签Block失败!");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_BLOCK_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_BLOCK_ERROR.getRespDesc());
        }
        rglog.info("加签Block:{}", signBlockString);

        /* 获取敏感信息加密证书 */
        String secPlanInfoListString = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.SEC_PLAN_INFO_LIST);
        List<SecPlanInfo> secPlanInfoList = JSONArray.parseArray(secPlanInfoListString, SecPlanInfo.class);
        Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(WechatParamConstant.SIGN_CERT_TYPE)).findFirst();
        if (secPlanInfoOpt.isPresent()) {
            CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
            CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(secPlanInfoOpt.get().getInstId() + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
            /* 加签 */
            WechatSignService wechatSignService = new WechatSignServiceImpl();
            try {
                //rglog.info("银联加签秘钥{}",certInfo.getCertValue());
                signData = wechatSignService.generateSignString(signBlockString, certInfo);
            } catch (Exception e) {
                rglog.error(e.getMessage());
                throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
            }
            rglog.info("数字签名:<{}>", signData);

        }
        if (signData == null) {
            rglog.info("生成数字签名失败");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        /* 将数字签名设置到报文中 */
        XmlDocument xmlDocument = new XmlDocument();
        XmlError xmlError = xmlDocument.loadString(xmlBefore);
        if (!xmlError.isSuccess()) {
            rglog.error("解析报文异常!");
            throw new BizException(RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode(), RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespDesc());
        } else {
            xmlDocument.setValue("sign", signData);
            /*登记业务报文表*/
            if (AppParamConstant.YES.equals(msgRegisterFlag)) {
                registerBizMsgInfo(rglog, xmlDocument.toString());
            }
            return xmlDocument.toString().getBytes();
        }
    }

    /**
     * 生成加签Block
     *
     * @param xmlString 原始报文
     * @return 加签BLOCK
     */
    private String generateSignBlockString(String xmlString) {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        try {
            if (null != xmlString && 0 != xmlString.trim().length()) {
                ArrayList<String> arrayList = new ArrayList<>();
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                DocumentBuilder documentBuilder = factory.newDocumentBuilder();
                ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xmlString.getBytes());
                Document document = documentBuilder.parse(byteArrayInputStream);
                NodeList allNodes = document.getFirstChild().getChildNodes();
                Node node;
                int i = 0;
                while (i < allNodes.getLength()) {
                    node = allNodes.item(i);
                    if (node instanceof Element) {
                        if (!"sign".equals(node.getNodeName()) && null != node.getTextContent() && 0 != node.getTextContent().length()) {
                            arrayList.add(node.getNodeName() + "=" + node.getTextContent() + "&");
                        }
                    }
                    i++;
                }
                String[] strArray = arrayList.toArray(new String[arrayList.size()]);
                Arrays.sort(strArray);
                StringBuffer stringBuffer = new StringBuffer();
                for (i = 0; i < strArray.length; i++) {
                    stringBuffer.append(strArray[i]);
                }
                return stringBuffer.substring(0, stringBuffer.length() - 1).toString();
            }
        } catch (Exception e) {
            rglog.error("系统异常!");
            return null;
        }
        return null;
    }

    private void registerBizMsgInfo(RgLogger rglog, String bizMsg) {
        try {
            XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
            if (bizMsg.length() > 8000) {
                rglog.error("存储转发报文超过8K，暂不登记到存储转发表:<{}>", bizMsg);
                return;
            }
            BizMsgInfo bizMsgInfo = new BizMsgInfo();
            BizMsgInfoMapper bizMsgInfoMapper = new BizMsgInfoMapperImpl();
            BaseRequest baseRequest = new BaseRequest();
            ConvertUtil.convertOutput(baseRequest);
            bizMsgInfo.setInstId(baseRequest.getHeader().getInstId());
            bizMsgInfo.setChannelId(xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.CHANNEL_ID));
            bizMsgInfo.setTransSeqNum(baseRequest.getHeader().getTraceNo());
            bizMsgInfo.setTxnDate(baseRequest.getHeader().getTransDate());
            bizMsgInfo.setTxnTime(baseRequest.getHeader().getTransTime());
            bizMsgInfo.setDstTxnNum(baseRequest.getHeader().getTxnNum());
            bizMsgInfo.setCreateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
            if (bizMsg.length() < 4000) {
                bizMsgInfo.setBizReqMsg(bizMsg);
            } else {
                bizMsgInfo.setBizReqMsg(bizMsg.substring(0, 4000));
                bizMsgInfo.setBizReqExpandMsg(bizMsg.substring(4000));
            }
            int iReturnCode = bizMsgInfoMapper.insertBizMsgInfo(bizMsgInfo);
            if (0 != iReturnCode) {
                return;
            }
            xmlTreeUtil.setXmlTreeObjectValue(WechatTreeNodeConstant.WECHAT_BIZ_MSG_INFO, bizMsgInfo);
        } catch (Exception e) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            e.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
            rglog.error("登记业务报文信息表异常:<{}>", byteArrayOutputStream.toString());
        }
    }

    private void updateBizMsgInfo(RgLogger rglog, String bizRspMsg) {
        try {
            XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
            if (bizRspMsg.length() > 8000) {
                rglog.error("存储转发报文超过8K，暂不登记到存储转发表:<{}>", bizRspMsg);
                return;
            }
            if (!xmlTreeUtil.isExist(WechatTreeNodeConstant.WECHAT_BIZ_MSG_INFO)) {
                rglog.error("获取请求业务报文节点失败，不更新");
                return;
            }
            BizMsgInfo bizMsgInfo = (BizMsgInfo) xmlTreeUtil.getXmlTreeObjectValue(WechatTreeNodeConstant.WECHAT_BIZ_MSG_INFO, BizMsgInfo.class);
            BizMsgInfoMapper bizMsgInfoMapper = new BizMsgInfoMapperImpl();
            bizMsgInfo.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
            if (bizRspMsg.length() < 4000) {
                bizMsgInfo.setBizRspMsg(bizRspMsg);
            } else {
                bizMsgInfo.setBizRspMsg(bizRspMsg.substring(0, 4000));
                bizMsgInfo.setBizRspExpandMsg(bizRspMsg.substring(4000));
            }
            int iReturnCode = bizMsgInfoMapper.updateBizMsgInfoByTransSeqNum(bizMsgInfo);
            if (0 != iReturnCode) {
                return;
            }
        } catch (Exception e) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            e.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
            rglog.error("登记业务报文信息表异常:<{}>", byteArrayOutputStream.toString());
        }
    }
}
